//
// Created on 2024/9/7.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include <iostream>
#include <random>
#include <string>
#include "peer_connection_dependency_factory_native.h"
#include "modules/audio_device/include/audio_device_data_observer.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
#include "api/create_peerconnection_factory.h"
#include "api/make_ref_counted.h"
#include "rtc_client/rtc_log.h"
#include "api/video_track_source_proxy_factory.h"
#include "media/base/adapted_video_track_source.h"
#include "rtc_base/timestamp_aligner.h"
#include "sdk/ohos/create_ohos_peerconnection_factory.h"

namespace ohosrtc {

std::shared_ptr<PeerConnectionDependencyFactoryNative> PeerConnectionDependencyFactoryNative::Create() {
  auto pcd_factory = std::make_shared<PeerConnectionDependencyFactoryNative>();
  pcd_factory->CreatePeerConnectionFactory();
  return pcd_factory;
}

void PeerConnectionDependencyFactoryNative::CreatePeerConnectionFactory() {
  if (!signaling_thread_.get()) {
    signaling_thread_ = ::rtc::Thread::Create();
    signaling_thread_->SetName("signaling_thread", nullptr);
    signaling_thread_->Start();
  }
  if (!network_thread_.get()) {
    network_thread_ = rtc::Thread::CreateWithSocketServer();
    network_thread_->SetName("network_thread", nullptr);
    network_thread_->Start();
  }
  
  if (!worker_thread_.get()) {
    worker_thread_ = rtc::Thread::Create();
    worker_thread_->SetName("worker_thread", nullptr);
    worker_thread_->Start();
  }

  auto peer_connection_factory = webrtc::CreatePeerConnectionFactory(
      network_thread_.get(), worker_thread_.get(), signaling_thread_.get(), adm_, 
      webrtc::CreateBuiltinAudioEncoderFactory(),
      webrtc::CreateBuiltinAudioDecoderFactory(),
      webrtc::CreateBuiltinVideoEncoderFactory(),
			webrtc::CreateBuiltinVideoDecoderFactory(),
      nullptr /*audio_mixer*/, nullptr /*audio_processing*/);
  if (!peer_connection_factory) {
    return;
  }
  pc_factory_ = peer_connection_factory;
}

PeerConnectionPtr PeerConnectionDependencyFactoryNative::CreatePeerConnection(RTCConfig config, PeerConnectionObserver *observer) {
  if (!pc_factory_) {
    return nullptr;
  }
  webrtc::PeerConnectionDependencies pc_dependencies(observer);
  auto pc_or_error = pc_factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
  return pc_or_error.MoveValue();
}

// create local stream include video/audio tracks used for publish
LocalStreamPtr PeerConnectionDependencyFactoryNative::CreateLocalStream(bool has_audio, bool video_capturer, bool is_screen_cast) {
  RTC_LOG << "create local stream has_audio=" << has_audio << " has_video=" << video_capturer;
  // create media stream
  auto local_stream = CreateLocalStream();
  if (has_audio) { // create audio track
    auto local_audio_track = CreateLocalAudioTrack();
    local_stream->MediaStream()->AddTrack(local_audio_track);
    // add audio track to media stream
  }
  if (video_capturer || is_screen_cast) { // create video track when have video capturer
    // create video source
    auto source = CreateVideoSource(is_screen_cast);
    if (is_screen_cast) {
      screen_source_ = source;
      auto local_video_track = CreateLocalVideoTrack(screen_source_);
      local_stream->MediaStream()->AddTrack(local_video_track);
    } else {
      video_source_ = source;
      auto local_video_track = CreateLocalVideoTrack(video_source_);
      local_stream->MediaStream()->AddTrack(local_video_track);
    }
  }
  
  local_stream_ = local_stream;
  return local_stream;
}

VideoTrackPtr PeerConnectionDependencyFactoryNative::CreateLocalVideoTrack(VideoSourcePtr video_source) {
  auto track_id = "video-track-" + GenerateRandomId(8);
  auto local_video_track = pc_factory_->CreateVideoTrack(video_source, track_id);
  return local_video_track;
}

VideoSourcePtr PeerConnectionDependencyFactoryNative::CreateVideoSource(bool is_screen_cast) {
  rtc::scoped_refptr<ohoswebrtc::OHOSVideoTrackSourceAdapter> video_track_source = webrtc::CreateVideoSourceTrackAdapter(is_screen_cast);
  rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> video_source = webrtc::CreateVideoTrackSourceProxy(signaling_thread_.get(), worker_thread_.get(), video_track_source.get());
  return video_source;
}

LocalStreamPtr PeerConnectionDependencyFactoryNative::CreateLocalStream() {
  auto stream_id = "stream-" + GenerateRandomId(8);
  auto local_media_stream = pc_factory_->CreateLocalMediaStream(stream_id);
  return std::make_shared<LocalStream>(local_media_stream);
}

AudioTrackPtr  PeerConnectionDependencyFactoryNative::CreateLocalAudioTrack() {
  cricket::AudioOptions options;
  auto audio_source = pc_factory_->CreateAudioSource(options);
  auto track_id = "audio-track-" + GenerateRandomId(8);
  auto local_audio_track = pc_factory_->CreateAudioTrack(track_id, audio_source.get());
  return local_audio_track;
}

std::string PeerConnectionDependencyFactoryNative::GenerateRandomId(int length) {
  const char characters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                            "abcdefghijklmnopqrstuvwxyz"
                            "0123456789";

  std::random_device rd;
  std::mt19937 gen(rd());
  std::uniform_int_distribution<> dis(0, sizeof(characters) - 2); // -2是因为最后一个字符是'\0'

  std::string randomString;

  for (size_t i = 0; i < length; ++i) {
    randomString += characters[dis(gen)];
  }

  return randomString;
}
} // namespace ohosrtc